#include "Parser.h"
#include "query.h"
#include "pf_defines.h"
using namespace hsql;

struct RelAttr {
  char *relName;     // relation name (may be NULL) 
  char *attrName;    // attribute name              
  friend ostream &operator<<(ostream &s, const RelAttr &ra);
};

struct Value {
  AttrType type;     // type of value               
  void     *data;    // value                       
  friend ostream &operator<<(ostream &s, const Value &v);
};

struct Condition {
  RelAttr lhsAttr;      // left-hand side attribute                     
  CompOp  op;           // comparison operator                          
  int     bRhsIsAttr;   // TRUE if right-hand side is an attribute
                        //   and not a value
  RelAttr rhsAttr;      // right-hand side attribute if bRhsIsAttr = TRUE
  Value   rhsValue;     // right-hand side value if bRhsIsAttr = FALSE
  friend ostream &operator<<(ostream &s, const Condition &c);
};


Parser::Parser()
{
	fm = new FileManager();
	bpm = new BufPageManager(fm);
	rmm = new RM_Manager(fm, bpm);
	ixm = new IX_Manager(fm, bpm);
	smm = new SM_Manager(ixm, rmm);
	qlm = new QL_Manager(rmm, smm);
}

Parser::~Parser()
{
	delete fm;
	delete bpm;
	delete rmm;
	delete ixm;
	delete smm;
	delete qlm;
}

void Parser::parse()
{
	int ret = 1;
	std::string sqlQuery;
	while (ret)
	{
		cout << ">> ";
		cin >> sqlQuery;
		ret = parseSQL(sqlQuery);
	}
}

int Parser::parseSQL(std::string sql)
{
	SQLParserResult* result = SQLParser::parseSQLString(sql);
	if (result->isValid)
	{
		for (SQLStatement *stmt : result->statements)
		{
			switch (stmt->type)
			{
			case StatementType::kStmtSingle:
				if (!processSingle((SingleStatement *)stmt))
					return 0;
				break;
			case StatementType::kStmtCreate:
				processCreate((CreateStatement *)stmt);
				break;
			case StatementType::kStmtDrop:
				processDrop((DropStatement *)stmt);
				break;
			case kStmtSelect:
				processSelect((SelectStatement *) stmt);
				break;
			case kStmtInsert:
				processInsert((InsertStatement *) stmt);
				break;
			case kStmtUpdate:
				processUpdate((UpdateStatement *) stmt);
				break;
			case kStmtDelete:
				processDelete((DeleteStatement *) stmt);
				break;
			default:
				break;
			}
		}
	}
	else {
		cout << "SQL(s) is(are) not valid...\n";
	}
}

int Parser::processSingle(hsql::SingleStatement *stmt)
{
	int ret = 1;
	switch (stmt->type)
	{
	case (SingleStatement::kCreateDatabase):
		smm->CreateDb(stmt->name);
		break;
	case (SingleStatement::kDropDatabase):
		smm->DropDb(stmt->name);
		break;
	case (SingleStatement::kUseDatabase):
		smm->OpenDb(stmt->name);
		break;
	case (SingleStatement::kShowDatabase):
	{
		cout << "Database: " << stmt->name << "\n";
		cout << "Table:\n";
		bool hasTables = false;
		for (string table : smm->GetTables(stmt->name))
		{
			hasTables = true;
			cout << table << "\n";
		}
		if (!hasTables) {
			cout << "No existing table. Please \"CREATE TABLE <tablename> ( columnlist );\"\n";
		}
			break;
	}
	case (SingleStatement::kShowTable):
	{
		int attrCount = 0;
		AttrInfo *attributes = NULL;
		if (smm->GetTableAttrInfo(NULL, stmt->name, attrCount, attributes) == OK)
		{
			cout << "Table: " << stmt->name << "\n";
			cout << "Column:\n";
			for (size_t i = 0; i < attrCount; i++)
			{
				cout << "Name: " << attributes[i].attrName << "\tType: ";
				switch (attributes[i].attrType)
				{
				case AttrType::DINT:
					cout << "DINT";
					break;
				case AttrType::DFLOAT:
					cout << "DFLOAT";
					break;
				case AttrType::STRING:
					cout << "STRING";
					break;
				case AttrType::VARCHAR:
					cout << "VARCHAR";
					break;
				case AttrType::NUL:
					cout << "NUL";
					break;
				default:
					break;
				}
				cout << "\n";
			}
			delete[] attributes;
		}
		break;
	}
	case (hsql::SingleStatement::kReadFile):
	{
		ifstream fin;
		string fileName =string(stmt->name) + ".sql";
		fin.open(fileName);
		if (fin)
		{
			string sqlQuery; string sql;
			while (getline(fin, sql))
			{
				sqlQuery += sql;
			}
			ret = parseSQL(sqlQuery);
			fin.close();
		}
		else
		{
			cout << fileName << " is not exists.\n";
		}

		break;
	}
	case (hsql::SingleStatement::kQuit):
		ret = 0;
	default:
		break;
	}
	return ret;
}

void Parser::processCreate(hsql::CreateStatement *stmt)
{
	AttrInfo *attributes = new AttrInfo[stmt->columns->size()];
	int i = 0;
	for (ColumnDefinition *column : *(stmt->columns))
	{
		attributes[i].attrName = column->name;
		attributes[i].attrType = (AttrType) column->type;
		switch (attributes[i].attrType)
		{
		case AttrType::STRING:
			attributes[i].attrLength = 256;
			break;
		case AttrType::VARCHAR:
			attributes[i].attrLength = column->width;
			break;
		default:
			attributes[i].attrLength = 4; // DFLOAT INT
			break;
		}
		attributes[i].notnull = column->notnull;
		i += 1;
	}
	smm->CreateTable(stmt->tableName, stmt->columns->size(), attributes);
	delete[] attributes;
}

void Parser::processDrop(hsql::DropStatement *stmt)
{
	smm->DropTable(stmt->name);
}

void Parser::processInsert(hsql::InsertStatement *stmt){
	Value myValues[10000];
	for(int i=0; i<stmt->values.size(); i++){
		for(int j=0; j<stmt->values[i].size; j++){
			switch(stmt->values[i][j]->type){
  				case(kExprLiteralInt):{
  					myValues[j].type = DINT;
  					myValues[j].data = stmt->values[i][j]->val;
  					break;
  				}
  				case(kExprLiteralFloat):{
  					myValues[j].type = FLOAT;
  					myValues[j].data = stmt->values[i][j]->value;
  					break;
  				}
  				case(kExprLiteralString):{
  					myValues[j].type = STRING;
  					myValues[j].data = stmt->values[i][j]->value;
  					break;
  				}
  				default:
  					break;
  			}
		}
  		switch(stmt->type){
			case(InsertStatement::kInsertValues):{
				qlm->Insert(stmt->tableName, stmt->values[i].size(), myValues);
				break;
			}
			case(InsertStatement::kInsertSelect):
				break;
			default:
				break;
		}
  	}
}

void Parser::processDelete(hsql::DeleteStatement *stmt){
	Condition myCondition[10000];
	for(int i=0; i<stmt->expr->size(); i++){
		myCondition[i].lhsAttr = stmt->expr[i]->expr->name;
		myCondition[i].op = NO_OP;		//default no comparison(when value is a null pointer)
		switch(stmt->expr->op_type){
			case(NOT_EQUALS):{
				myCondition[i].op = NE_OP;
				break;
			}
			case(LESS_EQ):{
				myCondition[i].op = LE_OP;
				break;
			}
			case(GREATER_EQ):{
				myCondition[i].op = GE_OP;
				break;
			}
			default:
				break;
		}
		switch(stmt->expr->op_char){
			case('='):{
				myCondition[i].op = EQ_OP;
				break;
			}
			case('<'):{
				myCondition[i].op = LT_OP;
				break;
			}
			case('>'):{
				myCondition[i].op = GT_OP;
				break;
			}
		}
		myCondition[i].bRhsIsAttr = FALSE;
  		myCondition[i].rhsAttr = NULL;
  		myCondition[i].rhsValue = stmt->expr->expr2->fval;
  	}
	qlm->Delete(stmt->tableName, stmt->expr->size(), myCondition);
}

void Parser::processUpdate(hsql::UpdateStatement* stmt){
	Condition myCondition[10000];
	for(int i=0; i<stmt->where->size(); i++){
		//condition
		myCondition[i].lhsAttr = stmt->updates[i]->column;
		myCondition[i].op = NO_OP;		//default no comparison(when value is a null pointer)
		switch(stmt->updates[i]->value->op_type){
			case(NOT_EQUALS):{
				myCondition[i].op = NE_OP;
				break;
			}
			case(LESS_EQ):{
				myCondition[i].op = LE_OP;
				break;
			}
			case(GREATER_EQ):{
				myCondition[i].op = GE_OP;
				break;
			}
			default:
				break;
		}
		switch(stmt->updates[i]->value->op_char){
			case('='):{
				myCondition[i].op = EQ_OP;
				break;
			}
			case('<'):{
				myCondition[i].op = LT_OP;
				break;
			}
			case('>'):{
				myCondition[i].op = GT_OP;
				break;
			}
		}
		myCondition[i].bRhsIsAttr = FALSE;
  		myCondition[i].rhsAttr = NULL;
  		myCondition[i].rhsValue = stmt->updates[i]->value->expr2->fval;
  		//updAttr
  		
	}
	for(int i=0; i<stmt->updates->size(); i++){
		RelAttr myUpdAttr;
  		myUpdAttr.relName = stmt->table;
  		myUpdAttr.attrName = stmt->updates[i]->column;
  		//rhsRelAttr
  		RelAttr myRhsRelAttr;
  		myRhsRelAttr.relName = stmt->table;
  		myRhsRelAttr.attrName = stmt->updates[i]->column;
  		//rhsValue
  		Value myRhsValue;
  		switch(stmt->updates[i]->value->type){
  			case(kExprLiteralInt):{
  				myRhsValue.type = DINT;
  				myRhsValue.data = stmt->updates[i]->value->val;
  				break;
  			}
  			case(kExprLiteralFloat):{
  				myRhsValue.type = FLOAT;
  				myRhsValue.data = stmt->updates[i]->value->value;
  				break;
  			}
  			case(kExprLiteralString):{
  				myRhsValue.type = STRING;
  				myRhsValue.data = stmt->updates[i]->value->value;
  				break;
  			}
  			default:
  				break;
  		}
		qlm->Update(stmt->table, myUpdAttr, 1, myRhsRelAttr, myRhsValue, 1,myCondition);
	}
}

void Parser::processSelect(hsql::SelectStatement* stmt){
	//selAttrs
	RelAttr mySelAttrs[10000];
	for(int i=0; i<stmt->selectList->size(); i++){
		mySelAttrs[i].relName = stmt->fromTable;
		mySelAttrs[i].attrName = stmt->selectList[i]->name;
	}
	Condition myCondition[10000];
	for(int i=0; i<stmt->whereClause->size(); i++){
		myCondition[i].lhsAttr = stmt->whereClause[i]->expr->name;
		myCondition[i].op = NO_OP;		//default no comparison(when value is a null pointer)
		switch(stmt->whereClause[i]->op_type){
			case(NOT_EQUALS):{
				myCondition[i].op = NE_OP;
				break;
			}
			case(LESS_EQ):{
				myCondition[i].op = LE_OP;
				break;
			}
			case(GREATER_EQ):{
				myCondition[i].op = GE_OP;
				break;
			}
			default:
				break;
		}
		switch(stmt->whereClause[i]->op_char){
			case('='):{
				myCondition[i].op = EQ_OP;
				break;
			}
			case('<'):{
				myCondition[i].op = LT_OP;
				break;
			}
			case('>'):{
				myCondition[i].op = GT_OP;
				break;
			}
		}
		myCondition[i].bRhsIsAttr = FALSE;
  		myCondition[i].rhsAttr = NULL;
  		myCondition[i].rhsValue = stmt->whereClause[i]->expr2->fval;
	}
	char* fromTable[10000];
	for(int i=0; i<stmt->fromTable->list->size(); i++){
		fromTable[i] = stmt->fromTable->list->getName();
	}
	qlm->Select(stmt->selectList->size(), mySelAttrs, stmt->fromTable->list->size(), fromTable, stmt->whereClause->size(), myCondition);
}
